采用 Org-publish 写博客

本站的内容使用 Emacs 编辑 org-mode 文件,使用 org-publish 导出为 html 文件,布署于 Github 之上。

我的博客源文档放置于私有 Github 仓库中,通过 Github actions 推送到开源的 jousimies.github.io 仓库中。

博客的架构如下:

├── build.el
├── init.el
├── org
│   ├── about.org
│   ├── index.org
│   └── posts
│       ├── 采用 Org-publish 写博客.org
│       └── 我的博客的CSS配置文件.org
└── static
    ├── js
    │   └── main.js
    └── style.css

Emacs 中的基本配置 init.el 内容如下:

(require 'ox-publish)

(defvar my/blog-nav-html
  "<a href=\"/index.html\">首页</a>
     <a href=\"/about.html\">关于</a>"
  "博客页面导航栏 HTML 片段。")

(setq org-publish-project-alist
      `(
        ("blog-org"
         :base-directory "./org/"
         :base-extension "org"
         :publishing-directory "./public/"
         :recursive t
         :html-head "<link rel=\"stylesheet\" href=\"/static/style.css\" />"
         :html-head-extra "<script src=\"/static/js/main.js\"></script>"
         :html-preamble nil
         :html-preamble-format (("en" ,my/blog-nav-html))
         :html-postamble t
         :html-postamble-format (("en" "<hr><div class=\"info\"> <span class=\"created\">Created with %c on MacOS</span>
 <span class=\"updated\">Updated: %d</span> </div>"))

         :with-toc t
         :section-numbers nil
         :with-author nil
         :with-creator nil
         :with-date t
         :publishing-function org-html-publish-to-html
         )

        ("blog-static"
         :base-directory "./static/"
         :base-extension "css\\|js\\|png\\|jpg\\|svg\\|ico"
         :publishing-directory "./public/static/"
         :recursive t
         :publishing-function org-publish-attachment)

        ("blog" :components ("blog-org" "blog-static"))))

(defun my/org-blog-update-recent ()
  "根据 org 文件更新时间生成 recent.org 文件,用于首页显示最近更新文章列表。"
  (interactive)
  (let* ((org-dir "./org")
         (files (directory-files (expand-file-name "posts" org-dir) t "\\.org$"))
         (recent-files
          (sort files
                (lambda (a b)
                  (time-less-p (nth 5 (file-attributes b))
                               (nth 5 (file-attributes a))))))
         (top-5 (seq-take recent-files 5)))
    (with-temp-file (expand-file-name "recent.org" org-dir)
      (insert "* 最近更新\n")
      (dolist (f top-5)
        (let* ((filename (file-name-nondirectory f))
               (title (with-temp-buffer
                        (insert-file-contents f)
                        (goto-char (point-min))
                        (if (re-search-forward "^#\\+TITLE: \\(.*\\)" nil t)
                            (match-string 1)
                          filename))))
          (insert (format "- [[file:posts\\%s][%s]]\n" filename title)))))))


(defun my/publish-blog ()
  "一键发布博客"
  (interactive)
  (org-publish "blog" t))

build.el 中的内容如下:

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)

(unless package-archive-contents
  (package-refresh-contents))

;; 安装 org 包(如果你依赖 org-plus-contrib,请根据需要改)
(unless (package-installed-p 'org)
  (package-install 'org))

(unless (package-installed-p 'htmlize)
  (package-install 'htmlize))

;; 加载你的 publish 设置
(load-file "init.el")

;; 执行发布
(org-publish-all t)

(message "Blog published successfully.")

默认导出的 html 文件非常的简单,需要结合 CSS 文件 和 JS 文件,以实现高阶的需求,比如界面的美化、回到上一页、回到首页的功能、侧边栏、动态目录等等。

关于 Github actions 的使用,问 Chatgpt 就好啦,需要注意使用 personal_token 。我的配置如下:

name: Deploy Blog

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Setup Emacs
        uses: purcell/setup-emacs@master
        with:
          version: 30.1

      - name: Install Org and dependencies
        run: emacs --batch -l build.el

      - name: Publish blog
        run: emacs --batch -l init.el -f org-publish-all

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          personal_token: ${{ secrets.GH_PAT }}
          publish_dir: ./public
          external_repository: Jousimies/jousimies.github.io
          publish_branch: main

Created with Emacs 30.1 (Org mode 9.7.11) on MacOS Updated: 2025-04-13 Sun 19:38